C# (江湖小新) 您所在的位置:网站首页 vc 等待线程结束 C# (江湖小新)

C# (江湖小新)

2023-04-14 20:48| 来源: 网络整理| 查看: 265

什么是Task?

描述

Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然可以满足基本业务场景,但它们在多个线程的等待处理方面、资源占用方面、延续和阻塞方面都显得比较笨拙,在面对复杂的业务场景下,显得有点捉襟见肘

Task是微软在.Net 4.0时代推出来的,也是微软极力推荐的一种多线程的处理方式,Task看起来像一个Thread,实际上,它是在ThreadPool的基础上进行的封装

Task的控制和扩展性很强,在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool

Task可以简单看作相当于Thead+TheadPool,其性能比直接使用Thread要更好,在工作中更多的是使用Task来处理多线程任务

任务Task和线程Thread的区别

Task是建立在Thread之上的,最终其实还是由Thread去执行,它们都是在System.Threading命名空间下的

Task跟Thread并不是一对一的关系。比如说开启10个任务并不一定会开启10个线程,因为使用Task开启新任务时,是从线程池中调用线程,这点与ThreadPool.QueueUserWorkItem类似

Task的使用创建Task的三种方式

方式一:通过创建Task对象后调用其Start()函数

方式二:调用Task的静态方法Run()

方式三:通过Task工厂,新建一个线程

// 方式一,通过StartTask t1 = new Task(() => { Console.WriteLine("我是Task方式一"); });t1.Start(); // 方式二,通过RunTask t2= Task.Run(()=>{Console.WriteLine("我是Task方式二"); }); // 方式三,通过工厂Task t3= Task.Factory.StartNew(()=>{Console.WriteLine("我是Task方式三"); });带返回值与不带返回值的Task

Task无返回值: 接收的是Action委托类型

Task有返回值:接收的是Func委托类型

static void Main(){ // 没有返回参数 Task t1 = new Task(() => { Console.WriteLine("我是Task没有返回参数"); }); t1.Start(); // 有返回参数 Task t2 = new Task(() => { return 1+1; }); t2.Start(); int result = t2.Result; Console.WriteLine(result);}

输出结果

我是Task没有返回参数2

一次性建立多个任务场景

static void test1(){ Task[] taskArray = new Task[10]; for (int i = 0; i < 10; i++) { int bb = i; Task t = Task.Run(() => { Console.WriteLine("任务ID:{0}, 结果:{1}",Thread.CurrentThread.ManagedThreadId, bb); }); taskArray[i] = t; } // 等待所有任务完成 Task.WaitAll(taskArray);}

输出结果

任务ID:4, 结果:0任务ID:10, 结果:4任务ID:7, 结果:1任务ID:8, 结果:2任务ID:10, 结果:7任务ID:11, 结果:5任务ID:9, 结果:3任务ID:12, 结果:6任务ID:7, 结果:8任务ID:8, 结果:9

Task阻塞的三种方式

Wait(): 等待单个线程任务完成

WaitAll():来指定等待的一个或多个线程结束

WaitAny():来指定等待任意一个线程任务结束

static void test3(){ // 方式一: wait方法 Task t = Task.Run(() => { Console.WriteLine("方式1:任务1......"); }) ; // 等待 上述任务完成 t.Wait(); Console.WriteLine("方式一结束.........."); // 方式二: waitAll 方法 Task tt = Task.Run(() => { Console.WriteLine("方式2:任务1......"); }); Task tt2 = Task.Run(() => { Console.WriteLine("方式2:任务2......"); }); Task.WaitAll(tt,tt2); Console.WriteLine("方式二结束.........."); // 方式三:waitAny 方法 Task ttt = Task.Run(() => { Console.WriteLine("方式3:任务1......"); }); Task ttt2 = Task.Run(() => { Console.WriteLine("方式3:任务2......"); }); Task.WaitAny(ttt, ttt2); Console.WriteLine("方式三结束.........."); }

输出结果

方式1:任务1......方式一结束..........方式2:任务1......方式2:任务2......方式二结束..........方式3:任务2......方式3:任务1......方式三结束..........

Task任务的延续

WhenAll().ContinueWith():作用是当WhenAll()中指定的线程任务完成后再执行ContinueWith()中的任务,也就是线程任务的延续。而由于这个等待是异步的,因此不会给主线程造成阻塞

WhenAll(task1,task2,...):Task的静态方法,作用是异步等待指定任务完成后,返回结果。当线程任务有返回值时,返回Task对象,否则返回Task对象。

WhenAny():用法与WhenAll()是一样的,不同的是只要指定的任意一个线程任务完成则立即返回结果。

ContinueWith():Task类的实例方法,异步创建当另一任务完成时可以执行的延续任务。也就是当调用对象的线程任务完成后,执行ContinueWith()中的任务

static void test4(){ Task t = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("我是线程任务....."); }); // 异步创建延续任务 Task.WhenAll(t).ContinueWith((data) => { Console.WriteLine("我是延续任务...."); }); Console.WriteLine("这是主线程........"); Console.ReadKey(); }

输出结果

这是主线程........我是线程任务.....我是延续任务....

注:Task任务的延续与上面阻塞相比,主要的好处就是延续是异步的不会阻塞主线程

Task的父子任务

TaskCreationOptions.AttachedToParent:用于将子任务依附到父任务中

static void test5(){ // 建立一个父任务 Task parentTask = new Task(() => { // 创建两个子任务,依附在父任务上 Task.Factory.StartNew(() => { Console.WriteLine("子task1任务。。。。。。"); }, TaskCreationOptions.AttachedToParent); Task.Factory.StartNew(() => { Console.WriteLine("子task2任务。。。。。。"); }, TaskCreationOptions.AttachedToParent); Thread.Sleep(1000); Console.WriteLine("我是父任务........"); }); parentTask.Start(); parentTask.Wait(); Console.WriteLine("这里是主线程......."); Console.ReadKey();}

输出结果

子task2任务。。。。。。子task1任务。。。。。。我是父任务........这里是主线程.......

Task中的任务取消Task中的取消功能使用的是CanclelationTokenSource,即取消令牌源对象,可用于解决多线程任务中协作取消和超时取消

CancellationToken Token:CanclelationTokenSource类的属性成员,返回CancellationToken对象,可以在开启或创建线程时作为参数传入。

IsCancellationRequested:表示当前任务是否已经请求取消。Token类中也有此属性成员,两者互相关联。

Cancel():CanclelationTokenSource类的实例方法,取消线程任务,同时将自身以及关联的Token对象中的IsCancellationRequested属性置为true。

CancelAfter(int millisecondsDelay):CanclelationTokenSource类的实例方法,用于延迟取消线程任务。

取消任务的两种情况

情况一:通过Cancel()方法

情况二:通过CancelAfter(milliseconds)方法

static void test6(){ // 情况一: 直接取消 // 创建取消令牌源对象 CancellationTokenSource cst = new CancellationTokenSource(); //第二个参数传入取消令牌 Task t = Task.Run(() => { while (!cst.IsCancellationRequested) { Thread.Sleep(500); Console.WriteLine("情况一,没有接收到取消信号......"); } }, cst.Token); Thread.Sleep(1000); //1秒后结束 cst.Cancel(); Console.ReadKey(); // 情况二: 延迟取消 CancellationTokenSource cst2 = new CancellationTokenSource(); Task t2 = Task.Run(() => { while (!cst2.IsCancellationRequested) { Console.WriteLine("情况二,没有接收到取消信号......"); } }, cst2.Token); //1秒后结束 cst2.CancelAfter(1000);Console.ReadKey();}Task跨线程访问界面控件通过 TaskScheduler.FromCurrentSynchronizationContext() 获取TaskScheduler,并将其放入Task的start()方法中或放入延续方法中即可

放入start()方法中

放入ContinueWith()延续方法中

// 通过start方法private void button1_Click(object sender, EventArgs e){ Task t = new Task(() => { // 为界面控件赋值 this.textBox1.Text = "线程内赋值"; }); task.Start(TaskScheduler.FromCurrentSynchronizationContext());} // 通过延续方法private void button1_Click(object sender, EventArgs e){ Task.Run(() => { Thread.Sleep(1000); }).ContinueWith(t => { this.textBox1.Text = "线程内赋值"; }, TaskScheduler.FromCurrentSynchronizationContext());}Task的异常处理异常捕获

Task线程的异常处理不能直接将线程对象相关代码try-catch来捕获,需要通过调用线程对象的wait()函数来进行线程的异常捕获

线程的异常会聚合到AggregateException异常对象中(AggregateException是专门用来收集线程异常的异常类),多个异常 需要通过遍历该异常对象来获取异常信息

如果捕获到线程异常之后,还想继续往上抛出,就需要调用AggregateException对象的Handle函数,并返回false。(Handle函数遍历了一下AggregateException对象中的异常)

static void test7(){ Task t = Task.Run(() => { throw new Exception("异常抛出....."); }); try { t.Wait(); } catch (AggregateException ex) { Console.Error.WriteLine(ex.Message); foreach (var item in ex.InnerExceptions) { Console.WriteLine("内异常:"+item.Message); } //将异常往外抛出 // ex.Handle(p => false); } Console.ReadKey();}输出结果One or more errors occurred. (异常抛出.....)内异常:异常抛出.....

欢迎大家点赞,留言,转发,转载,感谢大家的相伴与支持

激动的心,颤抖的手,点个【在看】有没有

大家看文章时有任何疑问,欢迎随时联系我,在公众号中回复“联系方式”即可获取我的微信号。或在公众号的菜单中点击联系我

也可以点击下方,发送消息 来私信我哦!!!!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有